import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * ParseurMaison est la classe permettant de lire et d'enregistrer un fichier XML
 * @author smathomeutbm
 * @version 1.0
 */
public class ParseurMaison {

	private Maison maison;
	private HashMap<String,Capteur> listeCapteurs;
	private HashMap<String,Objet> listeObjets;
	private HashMap<String,SourceEnergie> listeEnergie;
	private HashMap<String,ParametreExterieur> listeParametreExt;
	private Document dom;

	/**
	 * Constructeur par défaut de parseurMaison
	 */
	public ParseurMaison(){
		listeCapteurs = new HashMap<String,Capteur>();
		listeObjets = new HashMap<String,Objet>();
		listeEnergie = new HashMap<String,SourceEnergie>();
		listeParametreExt = new HashMap<String,ParametreExterieur>();
		maison = null;
	}

	/**
	 * Permettre l'ouverture d'un fichier et de le parser
	 * @param file Nom du fichier d'entrée
	 */
	public void loadFile(String file) throws BaliseXMLException{
		openXmlFile(file);
		parseDocument();
		System.out.println("Lecture du fichier XML : OK");
	}

	/**
	 * Ouvre un fichier passé en paramètre
	 * @throws BaliseXMLException Si le fichier comporte une erreur de syntaxe XML
	 */
	private void openXmlFile(String file) throws BaliseXMLException{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			//dom = db.parse("uneMaison.xml");
			dom = db.parse(file);
		}catch(ParserConfigurationException pce) {
			System.out.println("Je suis une exception de Parser");
			pce.printStackTrace();
		}catch(SAXException se) {
			throw new BaliseXMLException("syntaxe de fichier XML incorrecte");
		}catch(IOException ioe) {
			ioe.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Parse le fichier préalablement ouvert
	 * @throws BaliseXMLException Si jamais il manque une balise XML
	 */
	private void parseDocument() throws BaliseXMLException{
		String name, nameElement;

		Element docEle = dom.getDocumentElement();
		//VERIFIER LE NOM DE CETTE BALISE

		/*
		 * Récupération du nom de la maison
		 */
		name = docEle.getAttribute("name");
		if(name.equals(""))
			throw new BaliseXMLException("nom de la maison manquant");
		maison = new Maison(name);

		ParametreExterieur vent = null;
		ParametreExterieur luminosite = null;
		ParametreExterieur temperature = null;
		Capteur capteurExterieur = null;
		boolean capteurLumiere = false;
		boolean capteurVent = false;
		boolean capteurTemperature = false;
		
		
		/*
		 * Récupération des paramètres extérieurs
		 * et des capteurs associés
		 */
		NodeList nlParamExt = docEle.getElementsByTagName("paramExt");
		if(nlParamExt != null && nlParamExt.getLength() == 3){ //3 paramsExt obligatoires
			for(int i=0;i<nlParamExt.getLength();i++){
				Element el = (Element)nlParamExt.item(i);
				
				name = el.getAttribute("name");
				if(name.equals(""))
					throw new BaliseXMLException("nom d'un paramètre extérieur manquant");
				
				String strValeur = el.getAttribute("valeur");
				if(strValeur.equals(""))
					throw new BaliseXMLException("valeur d'un paramètre extérieur manquante");
				float valeur = Float.parseFloat(strValeur);
				
				if(name.equals("lumiere")){
					capteurLumiere = true;
					luminosite = new ParametreExterieur(name, valeur);
					listeParametreExt.put(name, luminosite);
					capteurExterieur = new Capteur("luminositeExt", luminosite, 0);
					listeCapteurs.put("luminositeExt", capteurExterieur);
					
				}else if (name.equals("vent")){
					capteurVent = true;
					vent = new ParametreExterieur("vent", valeur);
					listeParametreExt.put(name, vent);
					capteurExterieur = new Capteur("ventExt", vent, 0);
					listeCapteurs.put("ventExt", capteurExterieur);
					
				}else if (name.equals("temperature")){
					capteurTemperature = true;
					temperature = new ParametreExterieur(name, valeur);
					listeParametreExt.put(name, temperature);
					capteurExterieur = new Capteur("temperatureExt", temperature, 0);
					listeCapteurs.put("temperatureExt", capteurExterieur);
				}
				//System.out.println("Params Ext : " + name + " valeur=" + valeur);
				//System.out.println("Capteur Ext : " + name + " valeur=" + valeur);
			}
			if(!capteurLumiere)
				throw new BaliseXMLException("paramétre extérieur luminosite manquant");
			if(!capteurVent)
				throw new BaliseXMLException("paramétre extérieur vent manquant");
			if(!capteurTemperature)
				throw new BaliseXMLException("paramétre extérieur température manquant");
			
		}else{
			throw new BaliseXMLException("nombre de paramètre extérieur incorrect");
		}

		
		/*
		 * Récupération des énergies renouvalables
		 */
		NodeList nlEnergieRenouvelable = docEle.getElementsByTagName("renouvelable");
		if(nlEnergieRenouvelable != null && nlEnergieRenouvelable.getLength() == 2){
			
			boolean energieRenouvSolaire = false;
			boolean energieRenouvEolienne = false;
			
			for(int i=0;i<nlEnergieRenouvelable.getLength();i++){
				Element el = (Element)nlEnergieRenouvelable.item(i);
				
				name = el.getAttribute("name");
				if(name.equals(""))
					throw new BaliseXMLException("nom de l'énergie renouvelable manquant");
				
				String strEtat = el.getAttribute("etat");
				if(strEtat.equals(""))
					throw new BaliseXMLException("état de l'énergie renouvelable manquant");
				boolean etat = Boolean.parseBoolean(strEtat);
				
				if(name.equals("solaire")){
					energieRenouvSolaire = true;
					SourceEnergie er = new EnergieRenouvelable(name, etat, luminosite);
					listeEnergie.put(name,er);
					
				} else if(name.equals("eolienne")){
					energieRenouvEolienne = true;
					SourceEnergie er = new EnergieRenouvelable(name, etat, vent);
					listeEnergie.put(name,er);
				}
			}
			if(!energieRenouvEolienne)
				throw new BaliseXMLException("énergie renouvelable eolienne manquante");
			if(!energieRenouvSolaire)
				throw new BaliseXMLException("énergie renouvelable solaire manquante");
		}else{
			throw new BaliseXMLException("nombre d'énergies renouvelables incorrect");
		}

		
		/*
		 * Récupération des énergies indépendante
		 * comme le réseau électrique
		 */
		NodeList nlEnergieIndependante = docEle.getElementsByTagName("independante");
		if(nlEnergieIndependante != null && nlEnergieIndependante.getLength() > 0){
			
			boolean energieReseau = false;
			
			for(int i=0;i<nlEnergieIndependante.getLength();i++){
				Element el = (Element)nlEnergieIndependante.item(i);
				
				name = el.getAttribute("name");
				if(name.equals(""))
					throw new BaliseXMLException("nom de l'énergie dépendante manquant");
				
				if(name.equals("reseau"))
					energieReseau = true;
				
				String strEtat = el.getAttribute("etat");
				if(name.equals(""))
					throw new BaliseXMLException("état de l'énergie dépendante manquant");
				boolean etat = Boolean.parseBoolean(strEtat);
				
				SourceEnergie energie = new SourceEnergie(name, etat);
				listeEnergie.put(name,energie);
				//System.out.println("Independante name="+name+ " etat="+etat);
			}
			if(!energieReseau)
				throw new BaliseXMLException("énergie réseau électrique manquant");
		}else{
			throw new BaliseXMLException("nombre d'énergies dépendantes incorrect");
		}

		
		/*
		 * Récupération des pièces
		 */
		NodeList nlPiece = docEle.getElementsByTagName("piece");
		if(nlPiece != null && nlPiece.getLength() > 0){
			for(int i=0;i<nlPiece.getLength();i++){
				
				float temperaturePiece, temperatureVoulu;
				int coordHG, coordBD, id;
				boolean presence, lumiere;
				Element el = (Element)nlPiece.item(i);

				//Construction d'une pièce
				name = el.getAttribute("name");
				if(name.equals(""))
					throw new BaliseXMLException("nom de la pièce manquant");
				
				id = (i + 1);
				
				nameElement = el.getAttribute("coordBD");
				if(nameElement.equals(""))
					throw new BaliseXMLException("coordonnée bas/droit de la pièce manquante");
				coordBD = Integer.parseInt(nameElement);
				
				nameElement = el.getAttribute("coordHG");
				if(nameElement.equals(""))
					throw new BaliseXMLException("coordonnée haut/gauche de la pièce manquante");
				coordHG = Integer.parseInt(nameElement);
				
				nameElement = el.getAttribute("temperature");
				if(nameElement.equals(""))
					throw new BaliseXMLException("température de la pièce manquante");
				temperaturePiece = Float.parseFloat(nameElement);
				
				nameElement = el.getAttribute("temperatureVoulu");
				if(nameElement.equals(""))
					throw new BaliseXMLException("température voulu dans la pièce manquante");
				temperatureVoulu = Float.parseFloat(nameElement);
				
				nameElement = el.getAttribute("presence");
				if(nameElement.equals(""))
					throw new BaliseXMLException("présence dans la pièce manquante");
				presence = Boolean.parseBoolean(nameElement);
				
				nameElement = el.getAttribute("lumiere");
				if(nameElement.equals(""))
					throw new BaliseXMLException("lumière dans la pièce manquante");
				lumiere = Boolean.parseBoolean(nameElement);

				Piece piece = new Piece(name, (i+1), temperaturePiece, temperatureVoulu, presence, lumiere, coordHG, coordBD);
				
				maison.addPiece(piece);
				//System.out.println("Nom de la piece : " + name + " id=" + id + " coordHG = " + coordHG + " coordBD = " + coordBD + " Temperature : " + temperaturePiece + " Temperature voulu : " + temperaturePiece);

				
				
				/*
				 * Récupération des objets de la pièce 
				 */
				NodeList nlObjet = el.getElementsByTagName("objet");
				if(nlObjet != null && nlObjet.getLength() > 0){
					
					boolean objetFenetre = false;
					boolean objetLampe = false;
					boolean objetRadiateur = false;
					boolean objetVolet = false;
					
					for(int j=0;j<nlObjet.getLength();j++){
						Element elObjet = (Element)nlObjet.item(j);
						
						name = elObjet.getAttribute("name");
						if(name.equals(""))
							throw new BaliseXMLException("nom de l'objet manquant");
						if(name.equals("volet"))
							objetVolet = true;
						if(name.equals("radiateur"))
							objetRadiateur = true;
						if(name.equals("lampe"))
							objetLampe = true;
						if(name.equals("fenetre"))
							objetFenetre = true;
						name = name + "_" + id;
						
						
						nameElement = elObjet.getAttribute("etat");
						if(nameElement.equals(""))
							throw new BaliseXMLException("état de l'objet manquant");
						Boolean etat = Boolean.parseBoolean(nameElement);
						Objet objet = new Objet(name, etat);
						piece.putObjet(objet);
						listeObjets.put(name, objet);
						//System.out.println("Objet : "+ name + " etat=" + etat);
					}
					
					if(!objetLampe)
						throw new BaliseXMLException("objet lampe manquant");
					if(!objetFenetre)
						throw new BaliseXMLException("objet fenetre manquant");
					if(!objetVolet)
						throw new BaliseXMLException("objet volet manquant");
					if(!objetRadiateur)
						throw new BaliseXMLException("objet radiateur manquant");
					
				}else{
					throw new BaliseXMLException("nombre d'objets incorrect");
				}

				/*
				 * Récupération des capteurs de la pièce
				 */
				nlObjet = el.getElementsByTagName("capteur");
				if(nlObjet != null && nlObjet.getLength() > 0){
					
					boolean capteurPresencePiece = false;
					boolean capteurLumierePiece = false;
					boolean capteurTemperaturePiece = false;
					
					for(int j=0;j<nlObjet.getLength();j++){
						Element elObjet = (Element)nlObjet.item(j);
						
						name = elObjet.getAttribute("name");
						if(name.equals(""))
							throw new BaliseXMLException("nom de capteur manquant");
						if(name.equals("presence"))
							capteurPresencePiece = true;
						if(name.equals("temperature"))
							capteurTemperaturePiece = true;
						if(name.equals("lumiere"))
							capteurLumierePiece = true;
						name = name + "_" + id;
							
						nameElement = elObjet.getAttribute("tps");
						if(nameElement.equals(""))
							throw new BaliseXMLException("temps de capteur manquant");
						int tps=Integer.parseInt(nameElement);
						Capteur capteur = new Capteur(name, piece, tps);
						listeCapteurs.put(name, capteur);
						piece.addCapteur(capteur);
						//System.out.println("Capteur : "+ name + " tps="+tps);
					}
					
					if(!capteurLumierePiece)
						throw new BaliseXMLException("capteur lumière manquant");
					if(!capteurPresencePiece)
						throw new BaliseXMLException("capteur présence manquant");
					if(!capteurTemperaturePiece)
						throw new BaliseXMLException("capteur température manquant");
					
				}else{
					throw new BaliseXMLException("nombre de capteurs incorrect");
				}
			}
		}else{
			throw new BaliseXMLException("nombre de pièces incorrect");
		}
	}

	/**
	 * Enregistre la totalité de la maison
	 * @param file Nom de fichier de sortie
	 */
	public void saveFile(String file) {
		try {			
			//BufferedWriter fw = new BufferedWriter(new FileWriter(new File("save.xml")));
			BufferedWriter fw = new BufferedWriter(new FileWriter(new File(file)));
			String chaine = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
			chaine = chaine.concat(maison.xml());
			chaine = chaine.concat("\t<parametreExterieurs>\n");
			for(ParametreExterieur pe:listeParametreExt.values()){
				chaine = chaine.concat(pe.xml());
			}
			chaine = chaine.concat("\t</parametreExterieurs>\n");
			for(Piece piece:maison.getListePiece()){
				chaine = chaine.concat(piece.xml());
				for(Objet objet:piece.getListObjet().values()){
					chaine = chaine.concat(objet.xml());
				}
				for(Capteur capteur:piece.getListCapteur().values()){
					chaine = chaine.concat(capteur.xml());
				}
				chaine = chaine.concat("\t</piece>\n");
			}
			chaine = chaine.concat("\t<energie>\n");
			for(SourceEnergie energie:listeEnergie.values()){
				chaine = chaine.concat(energie.xml());
			}
			chaine = chaine.concat("\t</energie>\n");
			chaine = chaine.concat("</maison>");
			fw.write(chaine,0,chaine.length());
			fw.close();
			
			System.out.println("Enregistrement du fichier XML : OK");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Récupérer la maison
	 * @return La maison
	 */
	public Maison getMaison() {
		return maison;
	}

	/**
	 * Récupérer la liste des capteurs
	 * @return Liste de capteurs
	 */
	public HashMap<String, Capteur> getListeCapteurs() {
		return listeCapteurs;
	}

	/**
	 * Récupérer la liste des objets
	 * @return Liste des objets
	 */
	public HashMap<String, Objet> getListeObjets() {
		return listeObjets;
	}

	/**
	 * Récupérer la liste des énergies
	 * @return Liste des énergies
	 */
	public HashMap<String, SourceEnergie> getListeEnergie() {
		return listeEnergie;
	}

	/**
	 * Récupérer la liste des paramètres extérieurs
	 * @return Liste des paramètres extérieurs
	 */
	public HashMap<String, ParametreExterieur> getListeParametreExt() {
		return listeParametreExt;
	}
}